package com.kexilo.system.audit.service;



import com.kexilo.core.audit.domain.SysArchiveConfig;
import com.kexilo.core.audit.domain.SysDataArchive;
import com.kexilo.system.audit.mapper.SysArchiveConfigMapper;
import com.kexilo.system.audit.mapper.SysDataArchiveMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据归档服务实现
 *
 * @author Kexilo
 */
@Service
public class DataArchiveServiceImpl implements IDataArchiveService {
    
    private static final Logger log = LoggerFactory.getLogger(DataArchiveServiceImpl.class);

    @Autowired
    private SysDataArchiveMapper archiveMapper;

    @Autowired
    private SysArchiveConfigMapper configMapper;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /** 正在运行的归档任务 */
    private final Map<Long, CompletableFuture<Void>> runningTasks = new ConcurrentHashMap<>();

    @Override
    @Transactional
    public SysDataArchive executeArchive(SysArchiveConfig config) {
        SysDataArchive archive = new SysDataArchive();
        archive.setTaskName(config.getConfigName());
        archive.setTableName(config.getTableName());
        archive.setArchiveCondition(config.getArchiveCondition());
        archive.setArchiveType("1"); // 定时归档
        archive.setStatus("0"); // 进行中
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 保存归档记录
            archiveMapper.insert(archive);
            
            // 执行归档逻辑
            ArchiveResult result = performArchive(config);
            
            // 更新归档结果
            archive.setEndTime(LocalDateTime.now());
            archive.setArchiveCount(result.getArchivedCount());
            archive.setDeleteCount(result.getDeletedCount());
            archive.setArchiveFilePath(result.getFilePath());
            archive.setStatus("1"); // 成功
            archive.setDuration(System.currentTimeMillis() - startTime);
            
            archiveMapper.updateById(archive);
            
            log.info("数据归档成功：表={}, 归档数量={}, 删除数量={}", 
                    config.getTableName(), result.getArchivedCount(), result.getDeletedCount());
            
        } catch (Exception e) {
            // 更新失败状态
            archive.setEndTime(LocalDateTime.now());
            archive.setStatus("2"); // 失败
            archive.setErrorMessage(e.getMessage());
            archive.setDuration(System.currentTimeMillis() - startTime);
            archiveMapper.updateById(archive);
            
            log.error("数据归档失败：表={}", config.getTableName(), e);
            throw new RuntimeException("数据归档失败", e);
        }
        
        return archive;
    }

    @Override
    @Transactional
    public Long manualArchive(Long configId) {
        SysArchiveConfig config = configMapper.selectById(configId);
        if (config == null) {
            throw new RuntimeException("归档配置不存在");
        }
        
        if (!config.isEnabled()) {
            throw new RuntimeException("归档配置已禁用");
        }
        
        SysDataArchive archive = new SysDataArchive();
        archive.setTaskName("手动归档-" + config.getConfigName());
        archive.setTableName(config.getTableName());
        archive.setArchiveCondition(config.getArchiveCondition());
        archive.setArchiveType("2"); // 手动归档
        archive.setStatus("0"); // 进行中
        
        archiveMapper.insert(archive);
        
        // 异步执行归档
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                executeArchive(config);
            } catch (Exception e) {
                log.error("手动归档执行失败", e);
            }
        });
        
        runningTasks.put(archive.getArchiveId(), future);
        
        return archive.getArchiveId();
    }

    @Override
    public Map<String, Object> batchArchive() {
        List<SysArchiveConfig> enabledConfigs = configMapper.selectEnabledConfigs();
        
        Map<String, Object> result = new HashMap<>();
        result.put("totalConfigs", enabledConfigs.size());
        result.put("successCount", 0);
        result.put("failCount", 0);
        result.put("details", new HashMap<>());
        
        for (SysArchiveConfig config : enabledConfigs) {
            try {
                executeArchive(config);
                result.put("successCount", (Integer) result.get("successCount") + 1);
            } catch (Exception e) {
                result.put("failCount", (Integer) result.get("failCount") + 1);
                ((Map<String, String>) result.get("details")).put(config.getTableName(), e.getMessage());
            }
        }
        
        return result;
    }

    @Override
    public List<SysDataArchive> selectArchiveList(SysDataArchive archive) {
        return archiveMapper.selectArchiveList(archive);
    }

    @Override
    public SysDataArchive selectArchiveById(Long archiveId) {
        return archiveMapper.selectById(archiveId);
    }

    @Override
    @Transactional
    public int deleteArchiveByIds(Long[] archiveIds) {
        int count = 0;
        for (Long archiveId : archiveIds) {
            count += archiveMapper.deleteById(archiveId);
        }
        return count;
    }

    @Override
    public Map<String, Object> getArchiveStatistics(int days) {
        LocalDateTime startDate = LocalDateTime.now().minusDays(days);
        LocalDateTime endDate = LocalDateTime.now();
        
        Map<String, Object> statistics = archiveMapper.selectArchiveStatistics(startDate, endDate);
        List<Map<String, Object>> tableStats = archiveMapper.selectArchiveCountByTable(days);
        
        statistics.put("tableStatistics", tableStats);
        return statistics;
    }

    @Override
    @Transactional
    public int cleanExpiredArchiveRecords(int retentionDays) {
        LocalDateTime beforeTime = LocalDateTime.now().minusDays(retentionDays);
        return archiveMapper.deleteArchiveRecordsBefore(beforeTime);
    }

    @Override
    public boolean checkTableExists(String tableName) {
        try {
            String sql = "SELECT 1 FROM information_schema.tables WHERE table_name = ? AND table_schema = DATABASE() LIMIT 1";
            Integer result = jdbcTemplate.queryForObject(sql, Integer.class, tableName);
            return result != null;
        } catch (DataAccessException e) {
            return false;
        }
    }

    @Override
    public Map<String, Object> validateArchiveCondition(String tableName, String condition) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            if (!checkTableExists(tableName)) {
                result.put("valid", false);
                result.put("message", "表不存在：" + tableName);
                return result;
            }
            
            String sql = "SELECT COUNT(*) FROM " + tableName + " WHERE " + condition;
            Long count = jdbcTemplate.queryForObject(sql, Long.class);
            
            result.put("valid", true);
            result.put("count", count);
            result.put("message", "条件有效，匹配 " + count + " 条记录");
            
        } catch (DataAccessException e) {
            result.put("valid", false);
            result.put("message", "SQL语法错误：" + e.getMessage());
        }
        
        return result;
    }

    @Override
    public Map<String, Object> previewArchiveData(String tableName, String condition, int limit) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            String sql = "SELECT * FROM " + tableName + " WHERE " + condition + " LIMIT " + limit;
            List<Map<String, Object>> data = jdbcTemplate.queryForList(sql);
            
            result.put("success", true);
            result.put("data", data);
            result.put("count", data.size());
            
        } catch (DataAccessException e) {
            result.put("success", false);
            result.put("message", e.getMessage());
        }
        
        return result;
    }

    @Override
    public boolean stopArchiveTask(Long archiveId) {
        CompletableFuture<Void> future = runningTasks.get(archiveId);
        if (future != null) {
            boolean cancelled = future.cancel(true);
            if (cancelled) {
                runningTasks.remove(archiveId);
                
                // 更新任务状态为已取消
                SysDataArchive archive = archiveMapper.selectById(archiveId);
                if (archive != null && archive.isRunning()) {
                    archive.setStatus("3"); // 已取消
                    archive.setEndTime(LocalDateTime.now());
                    archive.setErrorMessage("用户手动停止");
                    archiveMapper.updateById(archive);
                }
            }
            return cancelled;
        }
        return false;
    }

    /**
     * 执行归档操作
     */
    private ArchiveResult performArchive(SysArchiveConfig config) {
        ArchiveResult result = new ArchiveResult();
        
        try {
            // 1. 查询待归档数据数量
            String countSql = "SELECT COUNT(*) FROM " + config.getTableName() + " WHERE " + config.getArchiveCondition();
            Long totalCount = jdbcTemplate.queryForObject(countSql, Long.class);
            
            if (totalCount == null || totalCount == 0) {
                log.info("表 {} 没有符合条件的数据需要归档", config.getTableName());
                return result;
            }
            
            // 2. 备份到文件（如果需要）
            if (config.needBackupToFile()) {
                String filePath = backupToFile(config);
                result.setFilePath(filePath);
            }
            
            // 3. 删除原数据（如果需要）
            if (config.shouldDeleteAfterArchive()) {
                String deleteSql = "DELETE FROM " + config.getTableName() + " WHERE " + config.getArchiveCondition();
                int deletedCount = jdbcTemplate.update(deleteSql);
                result.setDeletedCount((long) deletedCount);
            }
            
            result.setArchivedCount(totalCount);
            
        } catch (Exception e) {
            log.error("归档操作失败：表={}", config.getTableName(), e);
            throw new RuntimeException("归档操作失败", e);
        }
        
        return result;
    }

    /**
     * 备份数据到文件
     */
    private String backupToFile(SysArchiveConfig config) throws IOException {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        String fileName = config.getTableName() + "_archive_" + timestamp + ".sql";
        String backupPath = config.getBackupPath();
        
        if (backupPath == null || backupPath.trim().isEmpty()) {
            backupPath = System.getProperty("java.io.tmpdir") + "/archive/";
        }
        
        File backupDir = new File(backupPath);
        if (!backupDir.exists()) {
            backupDir.mkdirs();
        }
        
        String filePath = backupPath + fileName;
        
        try (FileWriter writer = new FileWriter(filePath)) {
            // 查询数据并写入文件
            String selectSql = "SELECT * FROM " + config.getTableName() + " WHERE " + config.getArchiveCondition();
            List<Map<String, Object>> data = jdbcTemplate.queryForList(selectSql);
            
            writer.write("-- 数据归档文件\n");
            writer.write("-- 表名: " + config.getTableName() + "\n");
            writer.write("-- 归档时间: " + LocalDateTime.now() + "\n");
            writer.write("-- 归档条件: " + config.getArchiveCondition() + "\n");
            writer.write("-- 数据量: " + data.size() + "\n\n");
            
            // 这里简化处理，实际可以生成标准的SQL INSERT语句
            for (Map<String, Object> row : data) {
                writer.write("-- " + row.toString() + "\n");
            }
        }
        
        return filePath;
    }

    /**
     * 归档结果内部类
     */
    private static class ArchiveResult {
        private Long archivedCount = 0L;
        private Long deletedCount = 0L;
        private String filePath;

        public Long getArchivedCount() {
            return archivedCount;
        }

        public void setArchivedCount(Long archivedCount) {
            this.archivedCount = archivedCount;
        }

        public Long getDeletedCount() {
            return deletedCount;
        }

        public void setDeletedCount(Long deletedCount) {
            this.deletedCount = deletedCount;
        }

        public String getFilePath() {
            return filePath;
        }

        public void setFilePath(String filePath) {
            this.filePath = filePath;
        }
    }
}
